#include "glwidget.h"

#ifdef WIN32
#include <gl/GLU.h>
#endif

GLWidget::GLWidget(QWidget *parent)
    : QGLWidget(parent)
{
    _scale = 100;
    _translateX = 0;
    _translateY = 0;

    _currentCoordsTransformer = 0;
    oneToOne();

    _selectedItem = -1;
    _grid.setStep(GRID_STEP, GRID_STEP);
    _grid.setGridLinesColor(QColor(200, 200, 200));
    _grid.setAxisColor(QColor(180, 180, 180));
}

GLWidget::~GLWidget()
{
    clear();
}

void GLWidget::addDrawable(Drawable *object)
{
    _drawables.push_back(object);
}


void GLWidget::initializeGL()
{    
    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    qglClearColor(QColor(Qt::white));
}

void GLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

void GLWidget::resizeGL(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-width/2, width/2, -height/2, height/2, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    _applyScaleAndTranslate();
}


void GLWidget::mousePressEvent(QMouseEvent *event)
{
    _lastPos = event->pos();
}

void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
    double dScale = _scale / 100.0;

    double dScaleX = (dScale*_scaleXMultiplier)/INITIAL_SCALE_MULTIPLIER;
    double dScaleY = (dScale*_scaleYMultiplier)/INITIAL_SCALE_MULTIPLIER;

    GLfloat dx = (GLfloat)(event->x() - _lastPos.x())/dScaleX;
    GLfloat dy = (GLfloat)(event->y() - _lastPos.y())/dScaleY;

    if(event->buttons() == Qt::LeftButton)
    {
        _translateX += dx;
        _translateY -= dy;
        _applyScaleAndTranslate();
    }
    _lastPos = event->pos();
}

void GLWidget::wheelEvent(QWheelEvent *event)
{
    if(event->delta() > 0)
    {
        _scale *= SCALE_STEP;

    }
    else
    {
        _scale /= SCALE_STEP;
    }
    if(_scale <= MIN_SCALE) _scale = MIN_SCALE;
    if(_scale >= MAX_SCALE) _scale = MAX_SCALE;

    _setGridStep();
    _applyScaleAndTranslate();
    updateGL();
}

void GLWidget::_calcTotalBounding()
{
    QRectF currentRect = _drawables[0]->getBoundingBox();
    QPointF maxLeftTop = currentRect.topLeft();
    QPointF maxRightBottom = currentRect.bottomRight();

    QPointF leftTop;
    QPointF rightBottom;

    for(int i=1; i<_drawables.size(); i++)
    {
        currentRect = _drawables[i]->getBoundingBox();
        leftTop = currentRect.topLeft();
        rightBottom = currentRect.bottomRight();
        if(leftTop.x() < maxLeftTop.x())
        {
            maxLeftTop.setX(leftTop.x());
        }
        if(leftTop.y() < maxLeftTop.y())
        {
            maxLeftTop.setY(leftTop.y());
        }
        if(rightBottom.x() > maxRightBottom.x())
        {
            maxRightBottom.setX(rightBottom.x());
        }
        if(rightBottom.y() > maxRightBottom.y())
        {
            maxRightBottom.setY(rightBottom.y());
        }
    }
    maxLeftTop += QPointF(-1, 1);
    maxRightBottom += QPointF(1, -1);
    if(_currentCoordsTransformer)
    {
        maxLeftTop = _currentCoordsTransformer->transform(maxLeftTop);
        maxRightBottom = _currentCoordsTransformer->transform(maxRightBottom);
    }
    _viewport = QRectF(maxLeftTop, maxRightBottom);
}

void GLWidget::_applyScaleAndTranslate()
{   
    double scale = _scale / 100.0;

    double scaleX = (scale*_scaleXMultiplier)/INITIAL_SCALE_MULTIPLIER;
    double scaleY = (scale*_scaleYMultiplier)/INITIAL_SCALE_MULTIPLIER;

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glScaled(scaleX, scaleY, 1);
    glTranslatef(_translateX, _translateY, 0);

    _grid.setBoundingBox(-_translateX, -_translateY, width()/scaleX, height()/scaleY);

    _setGridStep();
}

void GLWidget::_setGridStep()
{
    double scale = _scale / 100.0;

    double scaleX = (scale*_scaleXMultiplier)/INITIAL_SCALE_MULTIPLIER;
    double scaleY = (scale*_scaleYMultiplier)/INITIAL_SCALE_MULTIPLIER;


    double gridStepX = GRID_STEP / scaleX;
    double gridStepY = GRID_STEP / scaleY;


    gridStepX = _grid.getNearestHumaReadableStep(gridStepX);
    gridStepY = _grid.getNearestHumaReadableStep(gridStepY);

    _grid.setStep(gridStepX, gridStepY);
}


void GLWidget::autoScale()
{
    if(_drawables.empty()) return;

    _calcTotalBounding();
    _translateX = -(_viewport.left()+_viewport.right())/2.0;
    _translateY =  -(_viewport.bottom()+_viewport.top())/2.0;
    int viewPortHeight = abs(_viewport.height());
    int viewPortWidth = abs(_viewport.width());
    if(viewPortHeight >= viewPortWidth)
    {
        _scale = (100*height())/viewPortHeight;
    }
    else
    {
        _scale = (100*width())/viewPortWidth;
    }
    if(_scale <= MIN_SCALE) _scale = MIN_SCALE;
    if(_scale >= MAX_SCALE) _scale = MAX_SCALE;

    _applyScaleAndTranslate();
}

void GLWidget::clear()
{
    for(int i=0; i<_drawables.size(); i++)
    {
        delete _drawables[i];
    }
    _drawables.clear();
}

QPointF GLWidget::_projectWindowCoordinates(QPointF windowCoords)
{
    GLint viewport[4];
    GLdouble modelview[16];
    GLdouble projection[16];
    GLfloat winX, winY;
    GLdouble posX, posY, posZ;

    glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
    glGetDoublev( GL_PROJECTION_MATRIX, projection );
    glGetIntegerv( GL_VIEWPORT, viewport );

    winX = (float)windowCoords.x();
    winY = (float)viewport[3] - (float)windowCoords.y();

    gluUnProject(winX, winY, 0.0, modelview, projection, viewport, &posX, &posY, &posZ);

    return QPointF(posX, posY);
}

void GLWidget::switchToLinearCoords()
{
    _currentCoordsTransformer = 0;
    _grid.switchToLinearCoords();
}

void GLWidget::switchToLogarithmicCoords()
{
    _currentCoordsTransformer = &_logCoordsTransformer;
    _grid.switchToLogarithmicCoords();
}

void GLWidget::increaseXScale()
{
    _scaleXMultiplier*=2;
    _applyScaleAndTranslate();
    updateGL();
}

void GLWidget::decreaseXScale()
{
    _scaleXMultiplier/=2;
    if(_scaleXMultiplier <= 0)
    {
        _scaleXMultiplier = 1;
    }
    _applyScaleAndTranslate();
    updateGL();
}

void GLWidget::increaseYScale()
{
    _scaleYMultiplier*=2;
    _applyScaleAndTranslate();
    updateGL();
}

void GLWidget::decreaseYScale()
{
    _scaleYMultiplier/=2;
    if(_scaleYMultiplier <= 0)
    {
        _scaleYMultiplier = 1;
    }
    _applyScaleAndTranslate();
    updateGL();
}

void GLWidget::oneToOne()
{
    _scaleXMultiplier = INITIAL_SCALE_MULTIPLIER;
    _scaleYMultiplier = INITIAL_SCALE_MULTIPLIER;
    _applyScaleAndTranslate();
    updateGL();
}

void GLWidget::setEmfGrid(EmfGrid *grid)
{
    _emfGrid = grid;
}

EmfGrid *GLWidget::getEmfGrid()
{
    return _emfGrid;
}

